#include <iostream>
#include <string>
#include <fstream>
#include "grayscale.h"

using namespace std;


grayscale::grayscale(string imgName)
{
     name = imgName;

	 infile.open(imgName.c_str());

}

grayscale::~grayscale()
{
		for (int z = 0; z < numRows; z++)
			delete [] imgArray[z];
		delete [] imgArray;

                infile.close();
}
		
void grayscale::process()
{
    infile >> magicNum;
	infile >> numCols;
	infile >> numRows;
	infile >> maxVal;

	cout << "Image is " << numCols << "x" << numRows << endl << endl;
	
	imgArray = new int* [numRows];
	for (int i = 0; i < numRows; i++)
		imgArray[i] = new int [numCols];

	for (int r = 0; r < numRows ; r++)
		for (int c = 0; c < numCols ; c++)
			infile >> imgArray[r][c];

        infile.close();
				
	//Menu
	int menuChoice;
	char repeat;

	do{
		cout << "How would you like to transform " << name << "?" << endl << endl;
		
		cout << "1) Invert Image Colors" << endl;
		cout << "2) Flip Image Horizontally" << endl;
		cout << "3) Flip Image Vertically" << endl;
		cout << "4) Dither Image" << endl;
		cout << "5) Brighten Image" << endl;
		cout << "6) Darken Image" << endl;
		cout << "7) Contrast Image" << endl;
		cout << "8) Double Image Size" << endl;
		cout << "9) Shrink Image By Half" << endl;
		cout << "10) Save Image" << endl;
                cout << "11) Exit Program" << endl << endl;

		cout << "Please enter the number corresponding to your choice: ";
		cin >> menuChoice;

		while (menuChoice > 11 || menuChoice < 1){
			cout << "Invalid entry, please enter a valid choice: ";
			cin >> menuChoice;
		}

		switch(menuChoice){
			case 1: invert();
				cout << endl << "Image has been inverted, continue transforming (Y/N)? ";
				cin >> repeat;
				break;
			case 2: flipHorizontal();
				cout << endl << "Image has been flipped horizontally, continue transforming (Y/N)? ";
				cin >> repeat;
				break;
			case 3: flipVertical();
				cout << endl << "Image has been flipped vertically, continue transforming (Y/N)? ";
				cin >> repeat;
				break;
			case 4: dither();
				cout << endl << "Image has been dithered, continue transforming (Y/N)? ";
				cin >> repeat;
				break;
			case 5: brighten();
				cout << endl << "Image has been brightened, continue transforming (Y/N)? ";
				cin >> repeat;
				break;
			case 6: darken();
				cout << endl << "Image has been darkened, continue transforming (Y/N)? ";
				cin >> repeat;
				break;
			case 7: contrast();
				cout << endl << "Image has been contrasted, continue transforming (Y/N)? ";
				cin >> repeat;
				break;
			case 8: twicesize();
				cout << endl << "Image has been doubled in size, continue transforming (Y/N)? ";
				cin >> repeat;
				break;
			case 9: halfsize();
				cout << endl << "Image has been reduce in size by half, continue transforming (Y/N)? ";
				cin >> repeat;
				break;
			case 10: save();
				cout << endl << "Continue transforming (Y/N)? ";
				cin >> repeat;
				break;
            case 11: repeat = 'N';
				break;
		}
		system("cls");
	}while(toupper(repeat) == 'Y');
	
	cout << "Would you like to save? (Y/N): ";
	cin >> repeat;
	cout << endl;
	
	if (toupper(repeat) == 'Y')
		save();

}			

void grayscale::invert()
{
	for (int r = 0; r < numRows ; r++)
		for (int c = 0; c < numCols ; c++)
			imgArray[r][c] = maxVal - imgArray[r][c];

}

void grayscale::flipHorizontal()
{

        int** temp = new int* [numRows];
	for (int i = 0; i < numRows; i++)
		temp[i] = new int [numCols];


	for (int r = 0; r < numRows ; r++)
        {
                int tempC = (numCols - 1);
		for (int c = 0; c < numCols ; c++)
                {
                        temp[r][tempC] = imgArray[r][c];
                        tempC--;
                }
        }

        for (int s = 0; s < numRows ; s++)
		for (int d = 0; d < numCols ; d++)
                {
                        imgArray[s][d] = temp[s][d];
                }


        for (int z = 0; z < numRows; z++)
                delete [] temp[z];
        delete [] temp;

}

void grayscale::flipVertical()
{

        int** temp = new int* [numRows];
	for (int i = 0; i < numRows; i++)
		temp[i] = new int [numCols];


	for (int c = 0; c < numCols ; c++)
        {
                int tempR = (numRows - 1);
		for (int r = 0; r < numRows ; r++)
                {
                        temp[tempR][c] = imgArray[r][c];
                        tempR--;
                }

        }

        for (int r = 0; r < numRows ; r++)
		for (int c = 0; c < numCols ; c++)
                {
                        imgArray[r][c] = temp[r][c];
                }


        for (int z = 0; z < numRows; z++)
                delete [] temp[z];
        delete [] temp;

}

int nearest(float val)
{
	val = val + 0.5;
	int newVal = (int)val;

	if (newVal > 255)
		return 255;
	else if (newVal < 0)
		return 0;
	else
		return newVal; 
}

void grayscale::dither()
{
    int** temp = new int* [numRows];
	for (int y = 0; y < numRows; y++)
		temp[y] = new int [numCols];
		
	const int threshold = maxVal/2;
	
	for (int q = 0; q < numRows; q++)
		for (int w = 0; w < numCols; w++)
		{
			int oldpixel = imgArray[q][w];
			int error;

			if (oldpixel < threshold)
			{
				temp[q][w] = 0;
				error = oldpixel;
			}
			else
			{
				temp[q][w] = maxVal;
				error = oldpixel - maxVal;
			}
			
			if (q < numRows -1)
				imgArray[q+1][w] = nearest(imgArray[q+1][w] + ((7 * error)/16));

			if (q != 0 && w < numCols -1)
				imgArray[q-1][w+1] = nearest(imgArray[q-1][w+1] + ((3 * error)/16));

			if (w < numCols -1)
				imgArray[q][w+1] = nearest(imgArray[q][w+1] + ((5 * error)/16));

			if (q < numRows -1 && w < numCols -1)
				imgArray[q+1][w+1] = nearest(imgArray[q+1][w+1] + (error/16));
		}
		
		int** delImage = imgArray;
		imgArray = temp;
		
		for (int z = 0; z < numRows; z++)
			delete [] delImage[z];
		delete [] delImage;
				
}

void grayscale::save()
{
	string modName;
	char response;
	
	cout << "Save file as: ";
	cin >> modName;
	cout << endl;
	
	if (modName != name)
	{
		ofstream outfile(modName.c_str());
		
		outfile << magicNum << endl << numCols << " " << numRows << endl << maxVal << endl;

		for (int d = 0; d < numRows; d++)
		{
			for (int e = 0; e < numCols; e++)
			{
				outfile << imgArray[d][e] << " ";
			}
			outfile<< endl;
		}

		outfile.close();
	}
	else
	{
		cout << "Are you certain you want to overwrite the file " << name <<"? (Y/N): ";
		cin >> response;
		cout << endl;
		
		if (toupper(response) == 'Y')
		{

			string del = "del " + name;
			system(del.c_str());

			string cmd = "ren " + modName + " " + name;
			system(cmd.c_str());
			
			ofstream outfile(modName.c_str());
		
			outfile << magicNum << endl << numCols << " " << numRows << endl << maxVal << endl;

			for (int d = 0; d < numRows; d++)
			{
				for (int e = 0; e < numCols; e++)
				{
					outfile << imgArray[d][e] << " ";
				}
				outfile<< endl;
			}

			outfile.close();
		}
		else
			cout << "File was not saved." << endl;
	}
}

void grayscale::halfsize()
{
	int** newarr;
	
	newarr = new int*[numRows/2];
	for (int i = 0; i < (numRows/2); i++)
		newarr[i] = new int[numCols/2];

	int c1 = 0; int c2 = 0;

	for (int i = 0; i < numRows; i = (i+2))
	{
		for (int j = 0; j < numCols; j = (j+2))
		{
			newarr[c1][c2] = imgArray[i][j];
			c2++;
		}
		c1++;
		c2 = 0;
	}

	int** temp = imgArray;
	imgArray = newarr;

	for (int i = 0; i < numRows; i++)
			delete [] temp[i];
	delete [] temp;

	numRows = numRows/2;
	numCols = numCols/2;
}

void grayscale::twicesize()
{
	int** newarr;

	numRows = numRows*2;
	numCols = numCols*2;

	newarr = new int*[numRows];
	for (int i = 0; i < numRows; i++)
		newarr[i] = new int [numCols];

	int c1 = 0; int c2 = 0;

	for (int i = 0; i < numRows; i = (i+2))
	{
		for(int j = 0; j < numCols; j = (j+2))
		{
			newarr[i+1][j+1] = imgArray[c1][c2];
			newarr[i][j] = imgArray[c1][c2];
			c2++;
		}
		c1++;
		c2 = 0;
	}

	int** temp = imgArray;
	imgArray = newarr;

	for (int i = 0; i < numRows/2; i++)
			delete [] temp[i];
	delete [] temp;
}


void grayscale::brighten(){  //brightens image by 10%
	for (int i=0; i < numRows; i++){
		for(int j=0; j < numCols; j++){
			if(imgArray[i][j] >= 230){
				imgArray[i][j] = 255;
			}
			else{
				imgArray[i][j] += 25;
			}
		}
	}
}

void grayscale::darken(){  //Darkens image by 10%
	for (int i=0; i < numRows; i++){
		for(int j=0; j < numCols; j++){
			if(imgArray[i][j] <= 25){
				imgArray[i][j] = 0;
			}
			else{
				imgArray[i][j] -= 25;
			}
		}
	}
}

void grayscale::contrast(){  //Contrasts about 10%
	for (int i=0; i < numRows; i++){
		for(int j=0; j < numCols; j++){
			if(imgArray[i][j] >= 128){
				if (imgArray[i][j] >= 230)
					imgArray[i][j] = 255;
				else
					imgArray[i][j] = (float)imgArray[i][j] * 1.1;
			}
			else{
				imgArray[i][j] -= ((float)imgArray[i][j] * 0.1);
			}
		}
	}
}
